#include <time.h>
#include "../global/global_declarations.h"
#include "stdlib.h"
#include "string.h"
#include "ctype.h"
#include "stdio.h"
#include "../global/misc.h"
#include "../DEHT/deht.h"
#include "../global/global_auth.h"
#include "../exhaustive_search/dictionary.h"
#include "../exhaustive_search/rule.h"
#include "../DEHT/deht.h"
#include "../global/global_rainbow_table.h"


/**
 *checks the argv given in the command line.
 */
bool check_argv(int n, char* argv[]) {
	if (argv == 0 || n != 4) {
		fprintf(
				stderr,
				"Error: Usage create_rainbow_table <cryptographic hash> <output filenames prefix> <preferences filename>\n");
		return false;
	}
	if (check_Hash_name(argv[1]) && check_file(argv[3]) && !check_deht_files_exists(argv[2]))
		return true;
	return false;
}

/**
 *generates a random word from the rule given.
 */
void generate_random_password(String* dest, String* seed) {
	int64 i, rule_size;
	static int step = 0;
	rule_size = count_words_in_whole_rule(the_rule, the_dictionary);
	i = pseudo_random_generator_proj((unsigned char*) seed->data, seed->size,
			step);
	i = i % rule_size;
	word_k_for_rule(i, dest);
	step++;
}

/**
 *actually does the create rainbow table algorithm.
 *return 1 if succeeded and -1 if not.
 */
int pre_process(DEHT* deht, BasicHashFunctionPtr hash_func, int hash_length,
		int chain_length, String* seed) {
	int i;
	String* first_pass;
	int64* seeds_arr;
	int many_times;
	unsigned char result_of_chain_hashing[MAX_HASH_LENGTH] = {0};

	first_pass = make_empty_string();
	read_seeds(deht);
	seeds_arr = deht->seeds;

	many_times = (count_words_in_whole_rule(the_rule, the_dictionary) / chain_length +1)*10;
	for (i = 0; i < many_times; ++i) {
		generate_random_password(first_pass, seed);
		do_chain_hashing(chain_length, first_pass, seeds_arr, hash_func,
				hash_length, result_of_chain_hashing, true, 0);
		add_DEHT(deht, (unsigned char*) result_of_chain_hashing, hash_length,
				(unsigned char*) first_pass->data, first_pass->size);
		String_reset(first_pass);

	}
	String_free(first_pass);
	return 0;
}

int main(int argc, char* argv[]) {
	DEHT* deht;
	BasicHashFunctionPtr hash_func;
	IniFileContent ini_file;
	String* str_rule, *seed;
	int chain_length, bucket_block_length, hash_length;
	int64 entries_in_hash_table;

	if (!check_argv(argc, argv) || !read_ini_file(&ini_file, argv[3]))
		return 6;

	hash_length = hash_Length(argv[1]);
	hash_func = which_hash(argv[1]);
	the_dictionary = create_dictionary_from_file(ini_file.dictionary_filename);
	chain_length = ini_file.rainbow_chain_length;
	entries_in_hash_table = ini_file.entries_in_hash_table;
	bucket_block_length = ini_file.bucket_block_length;
	str_rule = make_string_from_char_arr(ini_file.rule);
	the_rule = make_rule_list_from_string(*str_rule);
	seed = make_string_from_char_arr(ini_file.main_random_seed);

	deht = create_empty_DEHT(argv[2], str_mod, keyTo64Bit, argv[1],
			entries_in_hash_table, bucket_block_length, 8, sizeof(int64)
					* chain_length);
	pre_process(deht, hash_func, hash_length, chain_length, seed);

	lock_DEHT_files(deht);
	String_free(str_rule);
	String_free(seed);
	free_dictionary(the_dictionary);
	free_rule(the_rule);
	free(deht);
	return 0;
}
